package no.uib.hplims.views.experiments;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import no.uib.hplims.MyVaadinApplication;
import no.uib.hplims.models.Experiment;
import no.uib.hplims.models.Peptide;
import no.uib.hplims.tools.ExportOrder;
import no.uib.hplims.tools.ExportExperiment;
import no.uib.hplims.tools.FileDownloadResource;
import no.uib.hplims.tools.Icons;
import no.uib.hplims.tools.columngenerators.NumberOfBlastHitsColumnGenerator;
import no.uib.hplims.tools.events.UpdatePeptideInformationEvent;
import no.uib.hplims.tools.events.UpdatePeptideInformationListener;
import no.uib.hplims.views.peptides.PeptideInformationView;

import org.vaadin.appfoundation.persistence.facade.FacadeFactory;
import org.vaadin.appfoundation.view.AbstractView;

import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Table;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.Notification;
import com.vaadin.ui.themes.BaseTheme;
import com.vaadin.ui.themes.Runo;

public class ExperimentView extends AbstractView<VerticalLayout> implements
		Button.ClickListener {

	private static final long serialVersionUID = 6295087536560780633L;

	private VerticalLayout layout = null;

	private Experiment experiment = null;

	private Label numberOfItemsLabel = null;
	private Table peptideTable = null;
	private BeanItemContainer<Peptide> peptideContainer = null;
	private final ArrayList<Object> visibleColumnIds = new ArrayList<Object>();
	private final ArrayList<String> visibleColumnLabels = new ArrayList<String>();

	private HorizontalLayout hl = null;
	private Label header = null;
	private TextField headerField = null;
	private Button editNameButton = null;
	private Button addPeptidesButton = null;
	private Button exportExperimentButton = null;
	private Button editButton = null;
	private Button refreshButton = null;
	private Button removeItemButton = null;

	private VerticalLayout peptideInformationArea = null;

	public ExperimentView(Experiment exp) {
		super(new VerticalLayout());
		experiment = exp;

		layout = getContent();
		layout.setMargin(true, true, true, true);

		setIcon(Icons.experimentTabIcon16);
		setCaption(experiment.getName());

		layout.addComponent(getToolbar());

		hl = new HorizontalLayout();
		hl.setSpacing(true);
		header = new Label(experiment.getName());
		header.setStyleName(Runo.LABEL_H2);
		hl.addComponent(header);

		editNameButton = new Button("Edit");
		editNameButton.addStyleName(BaseTheme.BUTTON_LINK);
		editNameButton.addListener(this);
		hl.addComponent(editNameButton);
		hl.setComponentAlignment(editNameButton, Alignment.MIDDLE_CENTER);
		layout.addComponent(hl);

		layout.addComponent(getEditToolbar());
		layout.addComponent(getPeptideTable());
	}

	private HorizontalLayout getToolbar() {
		HorizontalLayout toolbar = new HorizontalLayout();

		addPeptidesButton = new Button("Add");
		addPeptidesButton.setIcon(Icons.newItemIcon16);
		addPeptidesButton.addListener(this);
		toolbar.addComponent(addPeptidesButton);

		exportExperimentButton = new Button("Export");
		exportExperimentButton.addListener(this);
		toolbar.addComponent(exportExperimentButton);

		return toolbar;
	}

	private Component getEditToolbar() {
		final HorizontalLayout layout = new HorizontalLayout();
		layout.setSizeFull();

		final HorizontalLayout buttonLayout = new HorizontalLayout();

		editButton = new Button("Edit");
		editButton.setIcon(Icons.editIcon16);
		editButton.addListener(editButtonListener);
		buttonLayout.addComponent(editButton);

		refreshButton = new Button("Refresh");
		refreshButton.setIcon(Icons.refreshIcon16);
		refreshButton.addListener(this);
		buttonLayout.addComponent(refreshButton);

		removeItemButton = new Button("Remove Row");
		removeItemButton.setIcon(Icons.removeIcon16);
		removeItemButton.addListener(this);
		removeItemButton.setEnabled(false);
		buttonLayout.addComponent(removeItemButton);

		numberOfItemsLabel = new Label();
		numberOfItemsLabel.setSizeUndefined();

		layout.addComponent(buttonLayout);
		layout.addComponent(numberOfItemsLabel);
		layout.setComponentAlignment(numberOfItemsLabel, Alignment.BOTTOM_RIGHT);

		return layout;
	}

	private Table getPeptideTable() {
		if (peptideTable == null) {
			peptideTable = new Table();
			peptideTable.setStyleName(Runo.TABLE_SMALL);
			peptideTable.setSizeFull();
			peptideTable.setColumnReorderingAllowed(true);
			peptideTable.setImmediate(true);
			peptideTable.setSelectable(true);
			peptideTable.setMultiSelect(false);
			// Get experiments from DB and add to table
			peptideTable.setContainerDataSource(getPeptideContainer());

			visibleColumnIds.add("amountInMg");
			visibleColumnIds.add("barcodeID");
			visibleColumnIds.add("belongsToOrder");
			visibleColumnIds.add("cTerminal");
			visibleColumnIds.add("nTerminal");
			visibleColumnIds.add("id");
			visibleColumnIds.add("molecularWeight");
			visibleColumnIds.add("numBlastHits");
			visibleColumnIds.add("uniProtAccession");
			visibleColumnIds.add("peptideName");
			visibleColumnIds.add("peptideSequence");
			visibleColumnIds.add("sequenceLength");
			visibleColumnIds.add("wellID");

			visibleColumnLabels.add("Amount (mg)");
			visibleColumnLabels.add("Barcode");
			visibleColumnLabels.add("Order");
			visibleColumnLabels.add("C term");
			visibleColumnLabels.add("N term");
			visibleColumnLabels.add("ID");
			visibleColumnLabels.add("MW");
			visibleColumnLabels.add("# Blast hits");
			visibleColumnLabels.add("UniProt Acc #");
			visibleColumnLabels.add("Name");
			visibleColumnLabels.add("Sequence");
			visibleColumnLabels.add("Length");
			visibleColumnLabels.add("Position");

			peptideTable.setVisibleColumns(visibleColumnIds.toArray());
			peptideTable.setColumnHeaders(visibleColumnLabels
					.toArray(new String[0]));

			peptideTable.addGeneratedColumn("numBlastHits",
					new NumberOfBlastHitsColumnGenerator());

			peptideTable.addListener(new ValueChangeListener() {
				private static final long serialVersionUID = 875928070362720544L;

				public void valueChange(ValueChangeEvent event) {
					Peptide p = (Peptide) event.getProperty().getValue();
					if (p != null) {
						removeItemButton.setEnabled(true);
						if (peptideInformationArea == null) {
							peptideInformationArea = new PeptideInformationView(p,
									updatePeptideInformationListener);
							layout.addComponent(peptideInformationArea);
						} else {
							PeptideInformationView piv = new PeptideInformationView(p,
									updatePeptideInformationListener);
							layout.replaceComponent(peptideInformationArea, piv);
							peptideInformationArea = piv;
						}
					} else {
						removeItemButton.setEnabled(false);
					}
				}
			});
		}

		return peptideTable;
	}

	private BeanItemContainer<Peptide> getPeptideContainer() {
		if (peptideContainer == null) {
			peptideContainer = new BeanItemContainer<Peptide>(Peptide.class);

			// Get all experiments from DB and add to container
			List<Peptide> peptides = new ArrayList<Peptide>();
			for (Long id : experiment.getPeptideIds()) {
				peptides.add(FacadeFactory.getFacade().find(Peptide.class, id));
			}
			peptideContainer.addAll(peptides);

			updateItemsLabel();
		}

		return peptideContainer;
	}

	public void activated(Object... params) {

	}

	public void deactivated(Object... params) {

	}

	public void buttonClick(ClickEvent event) {
		if (event.getButton() == addPeptidesButton) {

			addPeptidesButtonClick();

		} else if (event.getButton() == refreshButton) {

			refreshButtonClick();

		} else if (event.getButton() == editNameButton) {

			editExperimentName();

		} else if (event.getButton().equals(exportExperimentButton)) {

			exportExperiment();

		} else if (event.getButton().equals(removeItemButton)) {
			Peptide p = (Peptide) peptideTable.getValue();
			experiment.removePeptide(p);
			FacadeFactory.getFacade().store(experiment);
			peptideTable.select(null);
			peptideTable.removeItem(p);
			layout.removeComponent(peptideInformationArea);
			peptideInformationArea = null;
		}

	}

	private void editExperimentName() {
		if (headerField == null) {
			headerField = new TextField();
			headerField.setValue((String) header.getValue());
			editNameButton.setCaption("OK");
			hl.replaceComponent(header, headerField);
		} else {
			if ((String) headerField.getValue() != "") {
				header.setValue((String) headerField.getValue());
				experiment.setName((String) header.getValue());
				editNameButton.setCaption("Edit");
				hl.replaceComponent(headerField, header);
				MyVaadinApplication.getInstance().getController()
						.updateExperiment(experiment);
			} else {
				hl.replaceComponent(headerField, header);
			}
			headerField = null;
		}
	}

	private void refreshButtonClick() {
		refreshExperimentContainer();
	}

	private void addPeptidesButtonClick() {
		// TODO: Not yet implemented
		getWindow()
				.showNotification(
						"Not yet implemented",
						"Please use orders or search results to add peptides to experiments.",
						Notification.TYPE_WARNING_MESSAGE);
	}

	private void refreshExperimentContainer() {
		peptideTable.removeAllItems();
		peptideContainer = null;
		peptideTable.setContainerDataSource(getPeptideContainer());
		// Sort the columns and make the headers nice
		peptideTable.setVisibleColumns(visibleColumnIds.toArray());
		peptideTable.setColumnHeaders(visibleColumnLabels
				.toArray(new String[0]));
	}

	private void updateItemsLabel() {
		numberOfItemsLabel.setValue("Items: " + peptideContainer.size());
	}

	private ClickListener editButtonListener = new ClickListener() {

		private static final long serialVersionUID = -4658724971914845340L;

		public void buttonClick(ClickEvent event) {
			Button btn = event.getButton();
			if (btn.getCaption().equals("Edit")) {
				getPeptideTable().setEditable(true);
				btn.setCaption("Save");
				btn.setIcon(Icons.saveIcon16);
			} else if (btn.getCaption().equals("Save")) {
				getPeptideTable().setEditable(false);
				btn.setCaption("Edit");
				btn.setIcon(Icons.editIcon16);
				FacadeFactory.getFacade().storeAll(
						getPeptideContainer().getItemIds());
			}
		}
	};

	private UpdatePeptideInformationListener updatePeptideInformationListener = new UpdatePeptideInformationListener() {

		public void eventOccurred(UpdatePeptideInformationEvent event) {
			// TODO: Update only the one peptide returned from the event
			refreshExperimentContainer();
		}
	};

	private void exportExperiment() {
		openExportWindow();
	}

	Button optimization = new Button("Optimization information");
	Button storage = new Button("Storage information");
	Window w;
	
	private void openExportWindow() {
		w = new Window("Export...");
		w.center();
		w.setWidth("300px");
		w.setModal(true);
		optimization.addListener(exportClickListener);
		storage.addListener(exportClickListener);
		w.addComponent(optimization);
		w.addComponent(storage);
		getWindow().addWindow(w);
	}
	
	private ClickListener exportClickListener = new ClickListener() {
		
		public void buttonClick(ClickEvent event) {
			// Create export object
			ExportExperiment exporter = new ExportExperiment();
			// Create file
			File f = null;
			if (event.getButton().equals(optimization)) {
				f = exporter.export(experiment);
			} else if (event.getButton().equals(storage)) {
				f = exporter.exportStorage(experiment);
			}
			
			if (f != null) {
				// Get application
				MyVaadinApplication app = MyVaadinApplication.getInstance();
				// Make file available for download
				getWindow().open(new FileDownloadResource(f, app));
			}
			if (w != null) {
				getWindow().removeWindow(w);
			}
		}
	};
}
